/*\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
\ win_adapter.h,  Adapters to redirect a C-style callback into a member fn
/				  and wrappers for common Win32 API calls that uses callbacks
\ 
/ Version:	1.0, 2001-07-02: created
\
/ Author:	Daniel Lohmann (daniel@losoft.de)
\			http://www.losoft.de/
/
\
/ Please send comments and bugfixes to the above email address.
\
/ This code is provided AS IS, you use it at your own risk! 
\ You may use it for whatever you want.
/
\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\*/

#ifndef __WIN_ADAPTER_H__
#define __WIN_ADAPTER_H__

#define WIN_ADAPTER_GENERATE_UNIQUE ( __LINE__ + 0x00000000 )

namespace win {

	namespace adapter {
		
		// The following struct is used as base class for all callback adapters
		// It just declares and initializes the adapters member data, so we don't have
		// to do this seperately for each single adapter. 
		template< class O, class CB, class U >
		struct CB_Base
		{
			typedef CB_Base super_t;

			CB_Base( O* pObj, CB pfCB, U UserData )
				: m_pObj( pObj ), m_pfCB( pfCB ), m_UserData( UserData ) {}

			O*	m_pObj;
			CB	m_pfCB;
			U	m_UserData;
		};

		/////////////////////////////////////////////////////////////////////////
		// Callback adapter for callback functions with a user defined argument.
		// This is any callback function following the form:
		//
		//     R CALLBACK MyCallback( [A1 arg1, A2 arg2, A3 arg2, ...], An UserData );
		//
		// where:
		//
		//     R		- return type of the callback fn
		//     A1		- type of the callbacks first argument
		//	   A2, ...  - type of the callbacks second, third, ... argument
		//	   An		- type of the callbacks last argument. This argument contains user defined
		//                data passed to the callback and is usually passed together with the
		//				  callbacks adress to the API that invokes the callback. An has to be
		//				  a 32 bit data type like void*, LONG, DWORD, etc.
		//
		// The adapter redirects the callback to a member fn of class O. 
		// This member fn can be any virtual or non-virtual function of the form:
		//
		//	   [virtual] R O::MyMemberCallback( [A1 arg1, A2 arg2, A3 arg2, ...], U UserData );
		//
		// where:
		//
		//	   R,A1...  - same as above
		//	   U		- type of some user defined data passed to the callback.
		//				  This replaces the An type of the original callback, but can be
		//				  any build in or user defined type
		//
		// The O, R, U, A1...An types are passed as template arguments to the adapter.
		//
		//////////////////
		// 
		// Example: Redirecting EnumWindows() callback to a member fn:
		//
		//	a) Choose an appropriate adapter:
		//     The prototype of the callback fn passed to EnumWindows is
		//         BOOL EnumWindowsProc( HWND hwnd, LPARAM lUserData )
		//     It takes two parameters, so use the CB2 adapter class.
		//  b) Choose the type of the user defined data.
		//     In the example an struct type is used. If you don't need user defined data     
		//	   choose int and pass 0 for the data.
		//
		//  struct userdata_t {
		//	    ...
		//  };
		//  class Test {
		//  public:
		//      BOOL MyEnumWindowsProc( HWND hWnd, userdata_t UserData ) {
		//			...
		//		}
		//		...
		//	};
		//
		//
		//  void foo() {
		//	    userdata_t UserData;
		//   	...
		//		Test t;
		//
		//		// Initialize Adapter
		//      win::adapter::CB2< Test, userdata_t, HWND, LPARAM> theAdapter( &t, &Test::MyEnumWindowsProc, UserData );
		//		// Call EnumWindows. Use adapters Callback fn and pass adapter instance for the
		//		// user defined data
		//      EnumWindows( theAdapter.Callback, (LPARAM) &theAdapter );
		//	}
		///////////////////////////////////////////////////////////////////////////
		
		
		// Adapter for callback functions with two arguments (last argument user defined).
		//     R CALLBACK MyCallback( A1 arg1, A2 Data ) is redirected to
		//     [virtual] R O::MyMemberCallback( A1 arg1, U UserData );
		template< class O, class U, class R, class A1, class A2 >
		struct CB2 : public CB_Base<O, R (O::*)(A1,U), U>
		{
			CB2( O* pObj, R (O::*pfCB)(A1, U), U UserData )
				: super_t( pObj, pfCB , UserData ) {}

			static R CALLBACK Callback( A1 arg1, A2 pObj )
			{
				CB2* _this = (CB2*)pObj;
				return (_this->m_pObj->*_this->m_pfCB)( arg1, _this->m_UserData );
			}
		};

		// Adapter for callback functions with three arguments (last argument user defined).
		//     R CALLBACK MyCallback( A1 arg1, A2 arg2, A3 Data ) is redirected to
		//     [virtual] R O::MyMemberCallback( A1 arg1, A2 arg2, U UserData );
		template< class O, class U, class R, class A1, class A2, class A3 >
		struct CB3 : public CB_Base<O, R (O::*)(A1,A2,U), U>
		{
			CB3( O* pObj, R (O::*pfCB)(A1, A2, U), U UserData )
				: super_t( pObj, pfCB , UserData ) {}

			static R CALLBACK Callback( A1 arg1, A2 arg2, A3 pObj )
			{
				CB3* _this = (CB3*)pObj;
				return (_this->m_pObj->*_this->m_pfCB)( arg1, arg2, _this->m_UserData );
			}
		};

		// Adapter for callback functions with four arguments (last argument user defined).
		//     R CALLBACK MyCallback( A1 arg1, A2 arg2, A3 arg3, A4 Data ) is redirected to
		//     [virtual] R O::MyMemberCallback( A1 arg1, A2 arg2, A3 arg3, U UserData );
		template< class O, class U, class R, class A1, class A2, class A3, class A4>
			struct CB4 : public CB_Base<O, R (O::*)(A1,A2,A3,U), U>
		{
			CB4( O* pObj, R (O::*pfCB)(A1, A2, A3, U), U UserData )
				: super_t( pObj, pfCB , UserData ) {}

			static R CALLBACK Callback( A1 arg1, A2 arg2, A3 arg3, A4 pObj )
			{
				CB4* _this = (CB4*)pObj;
				return (_this->m_pObj->*_this->m_pfCB)( arg1, arg2, arg3, _this->m_UserData );
			}
		};

		// Adapter for callback functions with five arguments (last argument user defined).
		//     R CALLBACK MyCallback( A1 arg1, A2 arg2, A3 arg3, A4 arg4, A5 Data ) is redirected to
		//     [virtual] R O::MyMemberCallback( A1 arg1, A2 arg2, A3 arg3, A4 arg4, U UserData );
		template< class O, class U, class R, class A1, class A2, class A3, class A4, class A5>
			struct CB5 : public CB_Base<O, R (O::*)(A1,A2,A3,A4,U), U>
		{
			CB5( O* pObj, R (O::*pfCB)(A1, A2, A3, A4, U), U UserData )
				: super_t( pObj, pfCB , UserData ) {}

			static R CALLBACK Callback( A1 arg1, A2 arg2, A3 arg3, A4 arg4, A5 pObj )
			{
				CB5* _this = (CB5*)pObj;
				return (_this->m_pObj->*_this->m_pfCB)( arg1, arg2, arg3, arg4, _this->m_UserData );
			}
		};

		// Adapter for callback functions with one user defined argument and adapter instance 
		// created on the heap. The adapter instance is automatically destroyed with delete
		// after processing the callback. This is used for CreateThread() and _beginthreadex
		//     R CALLBACK MyCallback( A1 arg1 ) is redirected to
		//     [virtual] R O::MyMemberCallback( U UserData );
		template< class O, class U, class R, class A1 >
		struct HeapCB1 : public CB_Base<O, R (O::*)(U), U>
		{
			static HeapCB1* Create( O* pObj, R (O::*pfCB)(U), U UserData )
			{
				return new HeapCB1( pObj, pfCB, UserData );
			}

			static R CALLBACK CallbackAndDestroy( A1  pObj )
			{
				HeapCB1* _this = (HeapCB1*)pObj;
				R Result = (_this->m_pObj->*_this->m_pfCB)(_this->m_UserData );
				delete _this;
				return Result;
			}
		protected:
			// Constructor is protected to prevent instances on the stack
			HeapCB1( O* pObj, R (O::*pfCB)(U), U UserData )
				: super_t( pObj, pfCB , UserData ) {}
		};

	
		/////////////////////////////////////////////////////////////////////////
		// Callback adapter for callback functions *without* a user defined argument.
		// This is any callback function following the form:
		//
		//     R CALLBACK MyCallback( [A1 arg1, A2 arg2, A3 arg2, ...] );
		//
		// The adapter redirects the callback to a member fn of class O. 
		// This member fn can be any virtual or non-virtual function of the form:
		//
		//	   [virtual] R O::MyMemberCallback( [A1 arg1, A2 arg2, A3 arg2, ...], U UserData );
		//
		//
		// Because there is no user defined data passed to the callbacks, it is not possible
		// to pass the adapters instance as an argument. Instead it is stored in a static 
		// data member. To ensure that the code also works in multi threaded environments, 
		// thread local storage is used for the static data member.
		// To ensure that not two different adapters, that are accidentally of the same type,
		// share the same class with the same instance of the static data, an additional int
		// template argument called INSTANCE is passed to the adapters. You have to ensure
		// that every instanciation of the template uses a different value for INSTANCE.
		//
		/////////////////////////////////////////////////////////////////////////

		template< int INSTANCE, class O, class U, class R, class A1 >
		struct StaticCB1 : public CB_Base<O, R (O::*)(A1,U), U> 
		{
			StaticCB1( O* pObj, R (O::*pfCB)(A1, U), U UserData )
				: super_t( pObj, pfCB , UserData )
			{
				StaticCB1::_this = this;
			}

			static R CALLBACK Callback( A1 arg1 )
			{
				return (_this->m_pObj->*_this->m_pfCB)(arg1, _this->m_UserData );
			}

			__declspec( thread ) static StaticCB1* _this;
		};
		template< int INSTANCE, class O, class U, class R, class A1 >
		StaticCB1<INSTANCE, O, U, R, A1>* StaticCB1<INSTANCE, O, U, R, A1>::_this = NULL;

		template< int INSTANCE, class O, class U, class R, class A1, class A2 >
		struct StaticCB2 : public CB_Base<O, R (O::*)(A1,A2,U), U> 
		{
			StaticCB2( O* pObj, R (O::*pfCB)(A1,A2,U), U UserData )
				: super_t( pObj, pfCB , UserData )
			{
				StaticCB2::_this = this;
			}

			static R CALLBACK Callback( A1 arg1, A2 arg2 )
			{
				return (_this->m_pObj->*_this->m_pfCB)(arg1, arg2, _this->m_UserData );
			}

			__declspec( thread ) static StaticCB1* _this;
		};
		template< int INSTANCE, class O, class U, class R, class A1, class A2 >
		StaticCB2<INSTANCE, O, U, R, A1, A2>* StaticCB2<INSTANCE, O, U, R, A1, A2>::_this = NULL;

	}	// namespace adapter


	///////////////////////////////////////////////////////////////////////////
	// Wrappers for APIs that use a callback with user defined 32 Bit data
	//
	
	template< class O, class U >
	BOOL EnumWindows( O* obj, BOOL (O::*pfMemFunc)(HWND, U), U UserData = U() )
	{
		typedef adapter::CB2<O, U, BOOL, HWND, LPARAM> adapter_t;
		return ::EnumWindows( adapter_t::Callback, (LPARAM) &adapter_t( obj, pfMemFunc, UserData ) );
	}

	template< class O, class U >
	BOOL EnumChildWindows( HWND hWnd, O* obj, BOOL (O::*pfMemFunc)(HWND, U), U UserData = U() )
	{
		typedef adapter::CB2<O, U, BOOL, HWND, LPARAM> adapter_t;
		return ::EnumChildWindows( hWnd, adapter_t::Callback, (LPARAM) &adapter_t( obj, pfMemFunc, UserData ) );
	}

	template< class O, class U >
	BOOL EnumDesktopWindows( HDESK hDesk, O* obj, BOOL (O::*pfMemFunc)(HWND, U), U UserData = U() )
	{
		typedef adapter::CB2<O, U, BOOL, HWND, LPARAM> adapter_t;
		return ::EnumDesktopWindows( hDesk, adapter_t::Callback, (LPARAM) &adapter_t( obj, pfMemFunc, UserData ) );
	}

	template< class O, class U >
	BOOL EnumDesktops( HWINSTA hWinSta, O* obj, BOOL (O::*pfMemFunc)(LPTSTR, U), U UserData = U() )
	{
		typedef adapter::CB2<O, U, BOOL, LPTSTR, LPARAM> adapter_t;
		return ::EnumDesktops( hWinSta, adapter_t::Callback, (LPARAM) &adapter_t( obj, pfMemFunc, UserData ) );
	}
	
	template< class O, class U >
	BOOL EnumWindowStations( O* obj, BOOL (O::*pfMemFunc)(LPTSTR, U), U UserData = U() )
	{
		typedef adapter::CB2<O, U, BOOL, LPTSTR, LPARAM> adapter_t;
		return ::EnumWindowStations( adapter_t::Callback, (LPARAM) &adapter_t( obj, pfMemFunc, UserData ) );
	}

	template< class O, class U >
	BOOL EnumResourceTypes( HMODULE hModule, O* obj, BOOL (O::*pfMemFunc)(HMODULE, LPTSTR, U), U UserData = U() )
	{
		typedef adapter::CB3<O, U, BOOL, HMODULE, LPTSTR, LONG_PTR> adapter_t;
		return ::EnumResourceTypes( hModule, adapter_t::Callback, (LONG_PTR) &adapter_t( obj, pfMemFunc, UserData ) );
	}

	template< class O, class U >
	BOOL EnumResourceNames( HMODULE hModule, LPCTSTR pszType, O* obj, BOOL (O::*pfMemFunc)(HMODULE, LPCTSTR, LPTSTR, U), U UserData = U() )
	{
		typedef adapter::CB4<O, U, BOOL, HMODULE, LPCTSTR, LPTSTR, LONG_PTR> adapter_t;
		return ::EnumResourceNames( hModule, pszType, adapter_t::Callback, (LONG_PTR) &adapter_t( obj, pfMemFunc, UserData ) );
	}

	template< class O, class U >
	BOOL EnumResourceLanguages( HMODULE hModule, LPCTSTR pszType, LPCTSTR pszName, O* obj, BOOL (O::*pfMemFunc)(HMODULE, LPCTSTR, LPCTSTR, WORD, U), U UserData = U() )
	{
		typedef adapter::CB5<O, U, BOOL, HMODULE, LPCTSTR, LPCTSTR, WORD, LONG_PTR> adapter_t;
		return ::EnumResourceLanguages( hModule, pszType, pszName, adapter_t::Callback, (LONG_PTR) &adapter_t( obj, pfMemFunc, UserData ) );
	}

	template< class O, class U >
	BOOL EnumThreadWindows( DWORD dwThreadId, O* obj, BOOL (O::*pfMemFunc)(HWND, U), U UserData = U() )
	{
		typedef adapter::CB2<O, U, BOOL, HWND, LPARAM> adapter_t;
		return ::EnumThreadWindows( dwThreadId, adapter_t::Callback, (LPARAM) &adapter_t( obj, pfMemFunc, UserData ) );
	}

	template< class O, class U >
	int EnumFonts( HDC hDC, LPCTSTR pszFaceName, O* obj, int (O::*pfMemFunc)(CONST LOGFONT*, CONST TEXTMETRIC*, DWORD, U), U UserData = U() )
	{
		typedef adapter::CB4<O, U, int, CONST LOGFONT*, CONST TEXTMETRIC*, DWORD, LPARAM> adapter_t;
		return ::EnumFonts( hDC, pszFaceName, adapter_t::Callback, (LPARAM) &adapter_t( obj, pfMemFunc, UserData ) );
	}

	template< class O, class U >
	int EnumFontFamiliesEx( HDC hDC, LPLOGFONT pLogFont, O* obj, int (O::*pfMemFunc)(ENUMLOGFONTEX*, NEWTEXTMETRICEX*, DWORD, U), U UserData = U(), DWORD dwFlags = 0 )
	{
		typedef adapter::CB4<O, U, int, ENUMLOGFONTEX*, NEWTEXTMETRICEX*, DWORD, LPARAM> adapter_t;
		return ::EnumFontFamiliesEx( hDC, pLogFont, (FONTENUMPROC) adapter_t::Callback, (LPARAM) &adapter_t( obj, pfMemFunc, UserData ), dwFlags );
	}

	template< class O, class U >
	BOOL EnumMetaFile( HDC hDC, HMETAFILE hmf, O* obj, int (O::*pfMemFunc)(HDC, HANDLETABLE*, METARECORD*, int, U), U UserData = U() )
	{
		typedef adapter::CB5<O, U, int, HDC, HANDLETABLE*, METARECORD*, int, LPARAM> adapter_t;
		return ::EnumMetaFile( hDC, hmf, adapter_t::Callback, (LONG) &adapter_t( obj, pfMemFunc, UserData ) );
	}

	template< class O, class U >	// Note: Order of parameters is slightly changed
	BOOL EnumEnhMetaFile( HDC hDC, HENHMETAFILE hemf, LPCRECT prcClip, O* obj, int (O::*pfMemFunc)(HDC, HANDLETABLE*, CONST ENHMETARECORD*, int, U), U UserData = U() )
	{
		typedef adapter::CB5<O, U, int, HDC, HANDLETABLE*, CONST ENHMETARECORD*, int, LPARAM> adapter_t;
		return ::EnumEnhMetaFile( hDC, hemf, adapter_t::Callback, (LPVOID) &adapter_t( obj, pfMemFunc, UserData ), prcClip );
	}

	template< class O, class U >
	int EnumICMProfiles( HDC hDC, O* obj, int (O::*pfMemFunc)(LPTSTR, U), U UserData = U() )
	{
		typedef adapter::CB2<O, U, int, LPTSTR, LPARAM> adapter_t;
		return ::EnumICMProfiles( hDC, adapter_t::Callback, (LPARAM) &adapter_t( obj, pfMemFunc, UserData ) );
	}

	template< class O, class U >
	int EnumObjects( HDC hDC, int nType, O* obj, int (O::*pfMemFunc)(LPVOID, U), U UserData = U() )
	{
		typedef adapter::CB2<O, U, int, LPVOID, LPARAM> adapter_t;
		return ::EnumObjects( hDC, nType, adapter_t::Callback, (LPARAM) &adapter_t( obj, pfMemFunc, UserData ) );
	}

	template< class O, class U >
	BOOL EnumerateLoadedModules( HANDLE hProcess, O* obj, BOOL (O::*pfMemFunc)(PSTR, ULONG, ULONG, U), U UserData = U() )
	{
		typedef adapter::CB4< O, U, BOOL, PSTR, ULONG, ULONG, PVOID> adapter_t;
		return ::EnumerateLoadedModules( hProcess, adapter_t::Callback, (PVOID) &adapter_t( obj, pfMemFunc, UserData ) );
	}

	template< class O, class U >
	BOOL EnumDisplayMonitors( HDC hDC, LPCRECT prcClip, O* obj, BOOL (O::*pfMemFunc)(HMONITOR, HDC, LPRECT, U), U UserData = U() )
	{
		typedef adapter::CB4<O, U, BOOL, HMONITOR, HDC, LPRECT, LPARAM> adapter_t;
		return ::EnumDisplayMonitors( hDC, prcClip, adapter_t::Callback, (DWORD) &adapter_t( obj, pfMemFunc, UserData ) );
	}


	///////////////////////////////////////////////////////////////////////////
	// Wrappers for APIs that use a callback that is called 
	// asynchronously from the current thread.
	//

	template< class O, class U > // Note: Order of parameters is changed
	HANDLE CreateThread( O* obj, DWORD (O::*pfThreadStart)(U), U UserData = U(), PDWORD pThreadId = NULL, DWORD dwCreationFlags = 0,  DWORD dwStackSize = 0, PSECURITY_ATTRIBUTES pSA = NULL )
	{
		typedef adapter::HeapCB1<O, U, DWORD, LPVOID> adapter_t;
		DWORD dwDummy;
		return ::CreateThread( pSA, dwStackSize, adapter_t::CallbackAndDestroy, 
							   (LPVOID) adapter_t::Create( obj, pfThreadStart, UserData ),
							   dwCreationFlags, pThreadId ? pThreadId : &dwDummy
				);
	}

	template< class O, class U > // Note: Order of parameters is changed
	HANDLE beginthreadex( O* obj, DWORD (O::*pfThreadStart)(U), U UserData = U(), PDWORD pThreadId = NULL, DWORD dwCreationFlags = 0,  DWORD dwStackSize = 0, PSECURITY_ATTRIBUTES pSA = NULL )
	{
		typedef adapter::HeapCB1<O, U, DWORD, LPVOID> adapter_t;
		DWORD dwDummy;
		return (HANDLE)::_beginthreadex( pSA, dwStackSize, (unsigned int (__stdcall *)(void *))adapter_t::CallbackAndDestroy, 
									     (LPVOID) adapter_t::Create( obj, pfThreadStart, UserData ),
									     dwCreationFlags, PUINT( pThreadId ? pThreadId : &dwDummy )
				);
	}


	///////////////////////////////////////////////////////////////////////////
	// Wrappers for APIs that use a callback without 
	// a user defined parameter. 
	//

	template< class O, class U >
	BOOL EnumSystemCodePages( O* obj, BOOL (O::*pfMemFunc)(LPTSTR, U), DWORD dwFlags, U UserData )
	{
		typedef adapter::StaticCB1< WIN_ADAPTER_GENERATE_UNIQUE, O, U, BOOL, LPTSTR > adapter_t;
		adapter_t theAdapter( obj, pfMemFunc, UserData );
		return ::EnumSystemCodePages( theAdapter.Callback, dwFlags );
	}

	template< class O, class U >
	BOOL EnumSystemLocales( O* obj, BOOL (O::*pfMemFunc)(LPTSTR, U), DWORD dwFlags, U UserData )
	{
		typedef adapter::StaticCB1< WIN_ADAPTER_GENERATE_UNIQUE, O, U, BOOL, LPTSTR > adapter_t;
		adapter_t theAdapter( obj, pfMemFunc, UserData );
		return ::EnumSystemLocales( theAdapter.Callback, dwFlags );
	}

	template< class O, class U >
	BOOL EnumDateFormats( O* obj, BOOL (O::*pfMemFunc)(LPTSTR, U), LCID Locale, DWORD dwFlags, U UserData = 0 )
	{
		typedef adapter::StaticCB1< WIN_ADAPTER_GENERATE_UNIQUE, O, U, BOOL, LPTSTR > adapter_t;
		adapter_t theAdapter( obj, pfMemFunc, UserData );
		return ::EnumDateFormats( theAdapter.Callback, Locale, dwFlags );
	}

	template< class O, class U >
	BOOL EnumDateFormatsEx( O* obj, BOOL (O::*pfMemFunc)(LPTSTR, U), LCID Locale, DWORD dwFlags, U UserData = 0 )
	{
		typedef adapter::StaticCB2< WIN_ADAPTER_GENERATE_UNIQUE, O, U, BOOL, LPTSTR, CALID > adapter_t;
		adapter_t theAdapter( obj, pfMemFunc, UserData );
		return ::EnumDateFormatsEx( theAdapter.Callback, Locale, dwFlags );
	}

	template< class O, class U >
	BOOL EnumTimeFormats( O* obj, BOOL (O::*pfMemFunc)(LPTSTR, U), LCID Locale, DWORD dwFlags, U UserData = 0 )
	{
		typedef adapter::StaticCB1< WIN_ADAPTER_GENERATE_UNIQUE, O, U, BOOL, LPTSTR > adapter_t;
		adapter_t theAdapter( obj, pfMemFunc, UserData );
		return ::EnumTimeFormats( theAdapter.Callback, Locale, dwFlags );
	}

	template< class O, class U >
	BOOL EnumCalendarInfo( O* obj, BOOL (O::*pfMemFunc)(LPTSTR, U), LCID Locale, CALID Calendar, CALTYPE CalType, U UserData = 0 )
	{
		typedef adapter::StaticCB1< WIN_ADAPTER_GENERATE_UNIQUE, O, U, BOOL, LPTSTR > adapter_t;
		adapter_t theAdapter( obj, pfMemFunc, UserData );
		return ::EnumCalendarInfo( theAdapter.Callback, Locale, Calendar, CalType );
	}


}	// namespace win


#endif // __WIN_ADAPTER_H__